package com.hui.auth.config.shiro;

import com.hui.auth.dao.AuthPermissionDao;
import com.hui.auth.dao.AuthRoleDao;
import com.hui.auth.dao.AuthUserDao;
import com.hui.auth.entity.AuthPermission;
import com.hui.auth.entity.AuthRole;
import com.hui.auth.entity.AuthUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 登录拦截器
 *
 * @author 廖云辉
 * <p>
 * shiro中唯一需要程序员编写的两个类:类AuthRealm完成根据用户名去数据库的查询,
 * 并且将用户信息放入shiro中,供第二个类调用.CredentialsMatcher,完成对于密码的校验.
 */
public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private AuthUserDao authUserDao;//此处如果直接注入服务，会存在该服务事务不生效的问题
    @Autowired
    private AuthPermissionDao permissionDao;
    @Autowired
    private AuthRoleDao roleDao;

    //认证.登录
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken utoken = (UsernamePasswordToken) token;//获取用户输入的token
        String username = utoken.getUsername();
        AuthUser user = authUserDao.selectByUserName(username);
        if (user == null) {
            throw new UnknownAccountException();//没找到帐号
        }
        List<AuthRole> roles = roleDao.selectByUid(user.getId());
        List<AuthPermission> permissions = permissionDao.selectByUid(user.getId());
        user.setRoles(roles);
        user.setPermissions(permissions);

        if (1 == user.getStatus()) {
            throw new LockedAccountException(); //帐号锁定
        }

        //保存user到session
        SecurityUtil.cacheUser(user);

        //放入shiro.调用CredentialsMatcher检验密码
        return new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), this.getClass().getName());
    }

    /**
     * 权限认证，为当前登录的Subject授予角色和权限
     *
     * @see 经测试：本例中该方法的调用时机为需授权资源被访问时
     * @see 经测试：并且每次访问需授权资源时都会执行该方法中的逻辑，这表明本例中默认并未启用AuthorizationCache
     * @see 经测试：如果连续访问同一个URL（比如刷新），该方法不会被重复调用，Shiro有一个时间间隔
     * （也就是cache时间，在ehcache-shiro.xml中配置），超过这个时间间隔再刷新页面，该方法会被执行
     */
    //授权,权限信息已经在获取用户信息的时候从数据库拿到，故此处不需要访问数据库,principal为配置的角色值
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //获取session中的用户
        AuthUser user = SecurityUtil.currentUser();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        /*处理角色信息*/
        List<String> roles = new ArrayList<>();
        for (AuthRole role : user.getRoles()) {
            roles.add(role.getName());
        }
        info.addRoles(roles);//将角色放入shiro中.
        /*处理权限信息*/
        List<String> perms = new ArrayList<>();
        for (AuthPermission permission : user.getPermissions()) {
            perms.add(permission.getUrl());
        }
        info.addStringPermissions(perms);//将权限放入shiro中.（默认权限信息为url）
        return info;
    }

}
